
import { defineAsyncComponent, ref, toRaw } from 'vue';
import Logic from '../logic/logic.mjs';
let DebugController = {
    breakpointListeners: new Map(),
    breakpoints: ref([]),
    registerBreakpointListener(key, listener) {
        this.breakpointListeners.set(key, listener);
    },
    unregisterBreakpointListener(key) {
        this.breakpointListeners.delete(key);
    },
    pushBreakpointAdd(breakpoint) {
        if (this.breakpointListeners) {
            for (let [key, listener] of this.breakpointListeners) {
                listener('add', breakpoint);
            }
        }
    },
    pushBreakpointRemove(breakpoint) {
        if (this.breakpointListeners) {
            for (let [key, listener] of this.breakpointListeners) {
                listener('remove', breakpoint);
            }
        }
    },


    getBreakPointsByScene(scene) {
        let srcList = scene.srcList;
        let usedbp = DebugController.breakpoints.value.filter((item) => { return srcList.indexOf(item.src) >= 0; }).map(toRaw);
        return usedbp;
    },
    getBreakPointsBySceneForDebugger(scene) {
        let breakpoints = DebugController.getBreakPointsByScene(scene);
        return breakpoints.map(DebugController.breakpointKey);
    },
    breakpointKey(breakpoint) {
        let info = [
            breakpoint.blockId,
            // breakpoint.type,
            breakpoint.codename,
            breakpoint.src,
            breakpoint.fsm,
            breakpoint.state,
            // breakpoint.func
        ];
        let str = JSON.stringify(info);
        return str;
    },
    createInfoByBlock(block) {
        let env = block.workspace._openblock_env;
        if (!env) return;
        let blockId = block.id;
        let {
            _openblock_src,
            _openblock_fsm,
            _openblock_state,
            _openblock_function,
            _openblock_type,
            _openblock_target,
        } = env;
        let breakpoint = {
            blockId,
            src: _openblock_src?.name,
            fsm: _openblock_fsm?.name,
            state: _openblock_state?.name,
            func: _openblock_function?.name,
            type: _openblock_type,
            codename: _openblock_target?.name,
            text: block.toString().substring(0, 15),
        };
        return breakpoint;
    },
    addBreakpoint(breakpoint) {
        if (DebugController.findBreakpoint(breakpoint) == -1) {
            DebugController.breakpoints.value.push(breakpoint);
            DebugController.breakpoints.value.sort((a, b) => {
                if (a.src < b.src) return -1;
                if (a.src > b.src) return 1;
                if (a.fsm < b.fsm) return -1;
                if (a.fsm > b.fsm) return 1;
                if (a.state < b.state) return -1;
                if (a.state > b.state) return 1;
                if (a.func < b.func) return -1;
                if (a.func > b.func) return 1;
                if (a.blockId < b.blockId) return -1;
                if (a.blockId > b.blockId) return 1;
                return 0;
            });
            DebugController.pushBreakpointAdd(breakpoint);
            return true;
        } else {
            return false;
        }
    },
    fire(block) {
        let event = new window.Blockly.Events.BlockBase(block);
        event.type = "BREAKPOINT_CHANGE";
        window.Blockly.Events.fire(event);
    },
    removeBreakpoint(breakpoint) {
        let idx = DebugController.findBreakpoint(breakpoint);
        if (idx >= 0) {
            DebugController.removeBreakpointByIdx(idx);
            return true;
        } else { return false; }
    },
    removeBreakpointByIdx(idx) {
        let breakpoint = DebugController.breakpoints.value.splice(idx, 1);
        if (breakpoint.length > 0) {
            DebugController.pushBreakpointRemove(toRaw(breakpoint[0]));
        }
    },
    toggleBreakpoint(breakpoint) {
        let idx = DebugController.findBreakpoint(breakpoint);
        if (idx >= 0) {
            DebugController.removeBreakpointByIdx(idx);
        } else {
            DebugController.addBreakpoint(breakpoint);
        }
    },
    findBreakpoint(breakpoint) {
        for (let idx in DebugController.breakpoints.value) {
            let bp = DebugController.breakpoints.value[idx];
            if (bp.blockId === breakpoint.blockId) {
                if (bp.src === breakpoint.src
                    && bp.fsm === breakpoint.fsm
                    && bp.state === breakpoint.state
                    && bp.func === breakpoint.func) {
                    return idx;
                }
            }
        }
        return -1;
    }
};
DebugController.installSider = () => {
    let DebugSider = defineAsyncComponent(async () => {
        let template = (await axios({
            url: 'js/htmls/debugger/DebugSider.html',
            responseType: 'text'
        })).data;
        return {
            template,
            data() {
                return {
                    breakpoints: DebugController.breakpoints
                }
            },
            computed: {
                srcs() {
                    let srcs = new Set(DebugController.breakpoints.value.map(b => b.src));
                    let srcs_a = Array.from(srcs);
                    srcs_a.sort();
                    return srcs_a;
                }
            },
            methods: {
                gotoBlock(block) {
                    let { src, fsm, state, codename, blockId } = block;
                    if (!fsm) {
                        Logic.instance.openFunction(src, codename, blockId);
                    } else if (!state) {
                        Logic.instance.openActionGroup(src, fsm, codename, blockId);
                    } else {
                        Logic.instance.openState(src, fsm, state, blockId);
                    }
                },
                blockOfSrc(src) {
                    return DebugController.breakpoints.value.filter(b => b.src === src);
                }
            }
        }
    });
    OB_IDE.addSider({
        icon: 'md-bug',
        component: 'DebugSider',
        name: OpenBlock.i('调试'),
    }, DebugSider);
};
DebugController.installContextMenu = () => {
    class DebugIcon extends Blockly.icons.Icon {
        // The constructor should always take in the source block so that svg elements
        // can be properly created.
        constructor(sourceBlock) {
            super(sourceBlock);
        }
        getType() {
            return new Blockly.icons.IconType('debug_icon');
        }
        initView(pointerdownListener) {
            if (this.svgRoot) return;  // Already initialized.

            // This adds the pointerdownListener to the svgRoot element.
            // If you do not call `super` you must do this yourself.
            super.initView(pointerdownListener);

            Blockly.utils.dom.createSvgElement(
                Blockly.utils.Svg.CIRCLE,
                {
                    'class': 'debug_icon',
                    'r': '8',
                    'cx': '0',
                    'cy': '0',
                    'stroke': 'white',
                    'fill': 'red',
                    'stroke-width': '1',
                },
                this.svgRoot  // Append to the svgRoot.
            );
        }
        onClick() {
            DebugController.removeBreakpoint(DebugController.createInfoByBlock(this.sourceBlock));
            DebugController.fire(this.sourceBlock);
        }
    }
    Blockly.icons.registry.register(
        new Blockly.icons.IconType('debug_icon'), DebugIcon);
    Blockly.Extensions.register('add_breakpoint_button', function () {
        let old_onochange = this.onchange;
        this.setOnChange(function (event) {
            if (old_onochange) { old_onochange.call(this, event); }
            if (event.type !== Blockly.Events.CREATE
                && event.type != "BREAKPOINT_CHANGE") {
                return; // 忽略其他事件
            }
            if (this.isInFlyout) return; // 不影响flyout中的块

            // 如果已经存在按钮则跳过
            if (!this.breakpointButton) {
                // 创建自定义按钮
                var buttonIcon = new DebugIcon(this);

                // 将按钮绑定到块上
                this.breakpointButton = buttonIcon;
                this.addIcon(buttonIcon);
            }

            let bpinfo = DebugController.createInfoByBlock(this);
            if (DebugController.findBreakpoint(bpinfo) >= 0) {
                this.breakpointButton.svgRoot.style.setProperty('display', 'block');
            } else {
                this.breakpointButton.svgRoot.style.setProperty('display', 'none');
            }
        });
    });
    Blockly.ContextMenuRegistry.registry.register({
        id: "toggleBreakpoint",
        scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
        displayText: OpenBlock.i('开关断点'),
        preconditionFn(scope) {
            // console.log(scope.block.workspace._openblock_env);
            if (scope?.block?.workspace?.internalIsFlyout) { return 'hidden'; }
            if (scope?.block?.outputConnection) { return 'hidden'; }
            let type = scope?.block?.workspace?._openblock_env?._openblock_type;
            if (!type) { return 'hidden'; }
            return type !== "struct" ? 'enabled' : 'hidden';
        },
        weight: 100,
        callback(e) {
            let bpinfo = DebugController.createInfoByBlock(e.block);
            DebugController.toggleBreakpoint(bpinfo);
            DebugController.fire(e.block);
        }
    });
    OpenBlock.wsBuildCbs.push((workspace) => {
        workspace.addChangeListener(function (event) {
            if (event.type === Blockly.Events.BLOCK_CREATE) {
                // 获取创建的块
                var blockIds = event.ids; // 新创建块的 ID 数组
                blockIds.forEach(function (id) {
                    var block = workspace.getBlockById(id);
                    if (block && !block.isInFlyout) {
                        // 应用扩展到该块
                        Blockly.Extensions.apply('add_breakpoint_button', block, false);
                    }
                });
            }
        });
    });
};
export default DebugController;